# The file controller contains the following actions:
# [#download]          downloads a file to the users system
# [#progress]          needed for upload progress
# [#upload]            shows the form for uploading files
# [#link]              shows the form for adding local files
# [#upload]            shows the form for uploading files
# [#do_the_link]       add link to a local file
# [#do_the_upload]     upload to and create a file in the database
# [#validate_filename] validates file to be uploaded
# [#rename]            show the form for adjusting the name of a file
# [#update]            updates the name of a file
# [#destroy]           delete files
# [#preview]           preview file; possibly with highlighted search words
class FileController < ApplicationController
  skip_before_filter :authorize, :only => :progress

  before_filter :does_folder_exist, :only => [:upload, :do_the_upload, :link, :do_the_link, :inbox] # if the folder DOES exist, @folder is set to it
  before_filter :does_file_exist, :except => [:upload, :progress, :do_the_upload, :link, :do_the_link, :validate_filename, :inbox] # if the file DOES exist, @myfile is set to it
  before_filter :authorize_creating, :only => [:upload, :link] # make it work with :link
  before_filter :authorize_reading, :only => [:download, :preview, :open_inline, :list_versions]
  before_filter :authorize_updating, :only => [:rename, :update]
  before_filter :authorize_deleting, :only => :destroy

  session :off, :only => :progress

  # The requested file will be downloaded to the user's system.
  # Which user downloaded which file at what time will be logged.
  # (adapted from http://wiki.rubyonrails.com/rails/pages/HowtoUploadFiles)
  def download
    log_attempt 
    unless STATIC_DOWNLOAD
      begin
        send_file @myfile.path, :filename => @myfile.filename
      rescue
        flash[:folder_error] = 'Transfer failed: you have been taken back to the root folder.'.t
        redirect_to :controller => 'folder', :action => 'list' and return false
      end 
    end
    @rel_link=@myfile.mklink
  end
  
  # The requested file will be opened in the browser
  # note that the client browser's configuration may come into play
  def open_inline
    if log_attempt
      send_file @myfile.path, :filename => @myfile.filename, :type => params[:mimetype], :disposition => 'inline'
    end
  end

  # Shows upload progress.
  # For details, see http://mongrel.rubyforge.org/docs/upload_progress.html
  def progress
    render :update do |page|
      @status = Mongrel::Uploads.check(params[:upload_id])
      page.upload_progress.update(@status[:size], @status[:received]) if @status
    end
  end

  # Shows the form where a user can select a new file to upload.
  def upload
    @myfile = Myfile.new
    if USE_UPLOAD_PROGRESS
      render
    else
      render :template =>'file/upload_without_progress'
    end
  end
  
  # Shows a form where one can add a list of files for indexing and downloading
  def link
    @myfile = Myfile.new
  end

  # Upload the file and create a record in the database.
  # The file will be stored in the 'current' folder.
  def do_the_upload
    if request.post?
      @myfile = Myfile.new(params[:myfile])
      @myfile.folder_id = folder_id
      @myfile.date_modified = Time.now
      @myfile.user = @logged_in_user
      
      assign_unique_version

      if @myfile.save
        if USE_UPLOAD_PROGRESS
          return_url = url_for(:controller => 'folder', :action => 'list', :id => folder_id)
          render :text => %(<script type="text/javascript">window.parent.UploadProgress.finish('#{return_url}');</script>)
        else
          redirect_to :controller => 'folder', :action => 'list', :id => folder_id
        end
      else
        render :template => 'file/upload_without_progress' unless USE_UPLOAD_PROGRESS
      end
    end
  end

  # Same as do_the_upload but links to a local file instead of having it uploaded
  def do_the_link
    if request.post?
      count=0
      lines=0
      (params['linklist']['linklist']).split("\n").each do |path|
        path.strip!
        lines +=1
        if LINK_WHITELIST.match(path) && File.exist?(path) && File.ftype(path)=='file'
          @myfile = Myfile.new
          @myfile.hardlink_to(path)
          @myfile.folder_id = folder_id
          @myfile.date_modified = Time.now
          @myfile.user = @logged_in_user

          assign_unique_version

          count += 1 if @myfile.save
        end
      end
      flash[:folder_error] = "File(s) succesfully added:".t + count.to_s + '/' + lines.to_s
      redirect_to :controller => 'folder', :action => 'list', :id => folder_id
    end
  end

  # create files out of incoming mail messages
  def inbox
    email_queue=MailHandler.check_mail
    email_queue.each do |message|
      email=message[:email]
      @myfile=Myfile.new
      @myfile.from_email(email)
      @myfile.folder_id = folder_id
      @myfile.tag_list.add("from=#{email.from}","id=#{email.message_id}")
      @myfile.date_modified = Time.now
      @myfile.user = @logged_in_user

      assign_unique_version

      @myfile.save
      message[:attachments].each do |raw|
         @myfile=Myfile.new
         @myfile.myfile=raw
         @myfile.tag_list.add("from=#{email.from}","id=#{email.message_id}")
         @myfile.folder_id = folder_id
         @myfile.user = @logged_in_user
         @myfile.date_modified = Time.now
         assign_unique_version
         @myfile.save
      end
    end
    redirect_to :controller => 'folder', :action => 'list', :id => params[:folder_id], :order_by => 'date_modified'
  end
  
  # Obsolete because of versioning: Validates a selected file in a file field via an Ajax call
  def validate_filename
    filename = CGI::unescape(request.raw_post).chomp('=')
    filename = Myfile.base_part_of(filename)
    render :text => %(<script type="text/javascript">document.getElementById('submit_upload').disabled=false;\nElement.hide('error');\nElement.hide('spinner');</script>)
  end

  # Show a form with the current name of the file in a text field.
  def rename
    render
  end

  # Update the name of the file with the new data.
  def update
    if request.post?
      new_name = Myfile.base_part_of(params[:myfile][:filename])
      check_version = (params[:ftarget] && params[:ftarget][:make_current]=='1') || (new_name != @myfile.filename) # must check versions when renaming too 
      @myfile.version = 0 if check_version
      @myfile.filename=new_name

      assign_unique_version if check_version

      if @myfile.update_attributes(:date_modified => Time.now, :tag_list => params[:myfile][:tag_list])
        redirect_to :controller => 'folder', :action => 'list', :id => folder_id
      else
        render_action 'rename'
      end
    end
  end

  # Preview file; possibly with highlighted search words.
  def preview
    if @myfile.indexed
      if params[:search].blank? # normal case
        @text = @myfile.text
      else # if we come from the search results page
        @text = @myfile.highlight(params[:search], { :field => :text, :excerpt_length => :all, :pre_tag => '[h]', :post_tag => '[/h]' })
      end
    end
  end

  # Delete a file.
  def destroy
    @myfile.destroy
    redirect_to :controller => 'folder', :action => 'list', :id => folder_id
  end

  # These methods are private:
  # [#does_file_exist]   Check if a file exists before executing an action
  # [#does_folder_exist] Check if a folder exists before executing an action
  # [#log_attempt]       Add an entry in the log
  private
    # Check if a file exists before executing an action.
    # If it doesn't exist: redirect to 'list' and show an error message
    def does_file_exist
      @myfile = Myfile.find(params[:id])
    rescue
      flash.now[:folder_error] = 'Someone else deleted the file you are using. Your action was cancelled and you have been taken back to the root folder.'.t
      redirect_to :controller => 'folder', :action => 'list' and return false
    end

    # Checks if a folder exists before executing an action.
    # If it doesn't exist: redirect to 'list' and show an error message
    def does_folder_exist
      if params[:folder_id].blank?
        super # Let ApplicationController handle it
      else
        begin
          @folder = Folder.find(params[:folder_id]) if params[:folder_id]
        rescue
          flash[:folder_error] = 'Someone else deleted the folder you are using. Your action was cancelled and you have been taken back to the root folder.'.t
          render :text => %(<script type="text/javascript">window.parent.location='#{url_for(:controller => 'folder', :action => 'list')}';</script>)
        end
      end
    end

    # Logs the 'usage' attempt
    def log_attempt
      usage = Usage.new
      usage.download_date_time = Time.now
      usage.user = @logged_in_user
      usage.myfile = @myfile
      return usage.save
    end
    
    # add a new version if filename already exists- NOTE: code duplication when USE_UPLOAD_PROGRESS is set?
    def assign_unique_version
      versions= Myfile.versions(@myfile)
      if versions.empty?
        @myfile.version = 0
      else # if previous versions exist, assign a version to the former version 0 (useful if implementing a different way to mark doc as deleted, later on)
        if versions.last.version == 0
          new_version_no = versions.first.version + 1
          versions.last.update_attribute(:version, new_version_no)
        end
        @myfile.tag_list = versions.last.tag_list if @myfile.tag_list.empty? #inherit tags
      end
    end
end